package com.wg.db.schema;

import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import com.wg.db.exception.DataException;
import com.wg.db.annotation.EntityField;
import com.wg.db.annotation.EntityTable;
import com.wg.db.ds.DataSourceMgr;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 实体映射封装
 * 支持线程安全的实体元数据缓存、字段自动扫描、异常增强及可访问性处理
 *
 * @author 少爷123
 */
public class SchemaHelper {
    /**
     * 缓存实体类到SchemaTable的映射（类名 -> SchemaTable）
     */
    private static final Map<String, SchemaTable> SCHEMA_TABLES = new ConcurrentHashMap<>();

    /**
     * 获取或创建实体的SchemaTable（线程安全）
     *
     * @param klass 实体类
     * @return 对应的SchemaTable实例
     */
    public static SchemaTable getOrCreateSchema(Class<?> klass) {
        validateEntityClass(klass);

        return SCHEMA_TABLES.computeIfAbsent(klass.getName(), key -> {
            EntityTable entityTable = klass.getAnnotation(EntityTable.class);
            SchemaTable schemaTable = new SchemaTable(klass);
            initSchemaTable(schemaTable, entityTable);

            scanAndRegisterColumns(schemaTable, klass);

            return schemaTable;
        });
    }

    /**
     * 校验实体类是否合法（必须标注@EntityTable）
     *
     * @param klass 实体类
     */
    private static void validateEntityClass(Class<?> klass) {
        if (!klass.isAnnotationPresent(EntityTable.class)) {
            throw new DataException("实体类[" + klass.getName() + "]未标注@EntityTable注解");
        }

        if (Arrays.stream(klass.getDeclaredConstructors())
                .noneMatch(c -> c.getParameterCount() == 0)) {
            throw new DataException("实体类[" + klass.getName() + "]必须包含无参构造函数");
        }
    }

    /**
     * 初始化SchemaTable的核心属性（表名、数据源、时间单位）
     *
     * @param schemaTable 实体元数据对象
     * @param entityTable 注解信息
     */
    private static void initSchemaTable(SchemaTable schemaTable, EntityTable entityTable) {
        String tableName = StrUtil.isEmpty(entityTable.name())
                ? klassToTableName(schemaTable.getKlass().getSimpleName())
                : entityTable.name();
        schemaTable.setTableName(tableName);

        String dataSourceName = entityTable.dataSource();
        Db db = DataSourceMgr.getInstance().getDataSource(dataSourceName);
        if (db == null) {
            throw new DataException("数据源[" + dataSourceName + "]不存在，实体类[" + schemaTable.getKlass().getName() + "]");
        }
        schemaTable.setDb(db);

        schemaTable.setTimeUnit(entityTable.timeUnit());
    }

    /**
     * 扫描实体类字段并注册SchemaColumn（处理主键、分组字段）
     *
     * @param schemaTable 实体元数据对象
     * @param klass       实体类
     */
    private static void scanAndRegisterColumns(SchemaTable schemaTable, Class<?> klass) {
        Arrays.stream(klass.getDeclaredFields())
                .filter(field -> !field.isSynthetic())
                .forEach(field -> processField(schemaTable, field));
    }

    /**
     * 处理单个字段，生成SchemaColumn并注册到SchemaTable
     *
     * @param schemaTable 实体元数据对象
     * @param field       实体类字段
     */
    private static void processField(SchemaTable schemaTable, Field field) {
        EntityField entityField = field.getAnnotation(EntityField.class);
        if (entityField == null) {
            return;
        }

        try {
            field.setAccessible(true);

            SchemaColumn column = new SchemaColumn(schemaTable, field, entityField);

            if (entityField.isKey()) {
                registerPrimaryKey(schemaTable, column, field.getName());
            }

            if (entityField.isGroupId()) {
                registerGroupField(schemaTable, column, field.getName());
            }

            schemaTable.addSchemaColumn(column);
        } catch (Exception e) {
            throw new DataException("处理字段[" + field.getName() + "]失败，实体类[" + schemaTable.getKlass().getName() + "]", e);
        }
    }

    /**
     * 注册主键字段（确保唯一）
     *
     * @param schemaTable 实体元数据对象
     * @param column      字段元数据
     * @param fieldName   字段名（用于异常提示）
     */
    private static void registerPrimaryKey(SchemaTable schemaTable, SchemaColumn column, String fieldName) {
        if (schemaTable.getPrimaryField() != null) {
            throw new DataException("实体类[" + schemaTable.getKlass().getName() + "]存在多个主键字段：" +
                    schemaTable.getPrimaryField().getName() + " 和 " + fieldName);
        }
        schemaTable.setPrimaryField(column);
    }

    /**
     * 注册分组字段（确保唯一）
     *
     * @param schemaTable 实体元数据对象
     * @param column      字段元数据
     * @param fieldName   字段名（用于异常提示）
     */
    private static void registerGroupField(SchemaTable schemaTable, SchemaColumn column, String fieldName) {
        if (schemaTable.getGroupField() != null) {
            throw new DataException("实体类[" + schemaTable.getKlass().getName() + "]存在多个分组字段：" +
                    schemaTable.getGroupField().getName() + " 和 " + fieldName);
        }
        schemaTable.setGroupField(column);
    }

    /**
     * 简单驼峰转下划线（可选工具方法，根据项目命名规范调整）
     *
     * @param camelCase 驼峰字符串（如userName）
     * @return 下划线字符串（如user_name）
     */
    private static String klassToTableName(String camelCase) {
        if (StrUtil.isBlank(camelCase)) {
            return camelCase;
        }
        StringBuilder tableName = new StringBuilder();
        for (char c : camelCase.toCharArray()) {
            if (Character.isUpperCase(c)) {
                tableName.append("_").append(Character.toLowerCase(c));
            } else {
                tableName.append(c);
            }
        }
        return tableName.toString().replaceFirst("_", "");
    }
}